{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于模型的协同过滤"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#导入相应的模块\n",
    "import pickle\n",
    "import numpy as np\n",
    "import scipy.io as sio\n",
    "import scipy.sparse as ss\n",
    "from numpy.random import random\n",
    "from collections import defaultdict\n",
    "from numpy import linalg as la"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基于SVD的协同过滤原则是将矩阵分解为两个矩阵相乘，使得相乘之后得到的矩阵与原矩阵中有值位置的值尽可能相同或相近，而相乘之后得到的值可以填补原矩阵中本来是缺失值的位置。这两个矩阵是通过梯度下降求得，由于隐含变量一般比原矩阵小很多，所以SVD有降维作用，可以减少计算量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class svdRecsys: #由于涉及到函数之间的调用，定义在同一个类中\n",
    "    def __init__(self):\n",
    "        ##读取已整理好数据文件\n",
    "        self.userIndex = pickle.load(open(\"PE_userIndex.pkl\", 'rb'))\n",
    "        self.itemIndex = pickle.load(open(\"PE_eventIndex.pkl\", 'rb'))\n",
    "        self.n_users = len(self.userIndex)\n",
    "        self.n_items = len(self.itemIndex)\n",
    "        self.userItemScores = sio.mmread(\"PE_userEventScores\").todense()\n",
    "        self.itemsForUser = pickle.load(open(\"PE_eventsForUser.pkl\", 'rb'))\n",
    "        self.usersForItem = pickle.load(open(\"PE_usersForEvent.pkl\", 'rb'))                  \n",
    "    \n",
    "\n",
    "    def train_SVD(self, K=30, steps=100,gamma=0.04,Lambda=0.15):\n",
    "        #定义所有的相关变量\n",
    "        self.K = K\n",
    "        self.bu = np.zeros(self.n_users) #用户属性值的初始化，共有n_users个用户\n",
    "        self.bi = np.zeros(self.n_items) #活动属性值的初始化，共有n_items个用户\n",
    "          \n",
    "        self.P = random((self.n_users,self.K))/10*(np.sqrt(self.K)) # P矩阵的初始值为满足高斯分布的随机值\n",
    "        self.Q = random((self.K, self.n_items))/10*(np.sqrt(self.K))  # Q矩阵的初始值为满足高斯分布的随机值\n",
    "        self.mu = 0.0 #打分平均的初始值\n",
    "        n_records = 0\n",
    "        uids = []  #用户的集合\n",
    "        iids = []  #活动的集合\n",
    "        \n",
    "        R = np.zeros((self.n_users, self.n_items)) #用户与活动关系矩阵的初始值，也可以不用初始化，在后面的代码中直接调用self.userItemScores[u,i]\n",
    "    \n",
    "        print(\"SVD training start now!\")\n",
    "        \n",
    "        f = open(\"train.csv\", 'r') \n",
    "        f.readline()  \n",
    "        for line in f:\n",
    "            cols = line.strip().split(\",\")\n",
    "            u = self.userIndex[cols[0]]  #用户\n",
    "            i = self.itemIndex[cols[1]] #活动\n",
    "            uids.append(u) #将读取的用户编号加入列表中\n",
    "            iids.append(i) #将读取的活动编号加入列表中  \n",
    "            R[u,i] = int(cols[4])  #打分值，或者调用self.userItemScores[u,i]\n",
    "            self.mu += R[u,i] \n",
    "            n_records += 1    \n",
    "        f.close()\n",
    "        self.mu /= n_records #计算n_recoreds个打分值的平均值\n",
    "    \n",
    "        for step in range(steps):  #利用迭代与梯度下降求出各变量的值\n",
    "            rmse_sum=0.0 #评估指标的初始值\n",
    "            kk = np.random.permutation(n_records) #打乱顺序，加入随机性  \n",
    "            for j in range(n_records):  \n",
    "                index = kk[j]  \n",
    "                u = uids[index]\n",
    "                i = iids[index]\n",
    "                eui = R[u,i] - self.pred_SVD(u,i) #真实值与预估值的差值\n",
    "                rmse_sum+=eui**2 #将差值转化为评估指标大小\n",
    "                self.bu[u]+= gamma*(eui - Lambda*self.bu[u]) #利用梯度下降求解bu\n",
    "                self.bi[i]+= gamma*(eui - Lambda*self.bi[i]) #利用梯度下降求解bi\n",
    "            \n",
    "                for k in range(self.K): #利用梯度下降求解分解矩阵\n",
    "                    self.P[u,k] += gamma * eui * self.Q[k,i] - Lambda * self.P[u,k]\n",
    "                    self.Q[k,i] += gamma * eui * self.P[u,k] - Lambda * self.Q[k,i]\n",
    "\n",
    "            gamma=gamma*0.93 #采用下降的学习率\n",
    "            print(\"the rmse of the {} th step on train data is:{}\".format(step, rmse_sum))\n",
    "        print (\"SVD trained\")\n",
    "    \n",
    "    def pred_SVD(self, uid, iid): #求出预估的打分值     \n",
    "        ans=self.mu + self.bi[iid] + self.bu[uid] + np.dot(self.P[uid,:],self.Q[:,iid])  \n",
    "        \n",
    "        #设定预估打分值的取值范围\n",
    "        if ans>1:  \n",
    "            return 1  \n",
    "        elif ans<0:  \n",
    "            return 0\n",
    "        return ans\n",
    "    \n",
    "    def svd_Based_CF(self, userId, eventId): #利用模型进行预测\n",
    "        u = self.userIndex[userId]\n",
    "        i = self.itemIndex[eventId]\n",
    "        return self.pred_SVD(u,i)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SVD training start now!\n",
      "the rmse of the 0 th step on train data is:7911.402653905758\n",
      "the rmse of the 1 th step on train data is:2846.980898970227\n",
      "the rmse of the 2 th step on train data is:2409.4795843155434\n",
      "the rmse of the 3 th step on train data is:2244.9053407441615\n",
      "the rmse of the 4 th step on train data is:2112.787681312904\n",
      "the rmse of the 5 th step on train data is:2002.0006662073413\n",
      "the rmse of the 6 th step on train data is:1915.2729509110327\n",
      "the rmse of the 7 th step on train data is:1846.0277234908265\n",
      "the rmse of the 8 th step on train data is:1786.210723612353\n",
      "the rmse of the 9 th step on train data is:1737.1200546438206\n",
      "the rmse of the 10 th step on train data is:1694.1163553408678\n",
      "the rmse of the 11 th step on train data is:1656.5102387936133\n",
      "the rmse of the 12 th step on train data is:1624.264008358685\n",
      "the rmse of the 13 th step on train data is:1594.651565966491\n",
      "the rmse of the 14 th step on train data is:1569.7180601861483\n",
      "the rmse of the 15 th step on train data is:1547.5636210464459\n",
      "the rmse of the 16 th step on train data is:1525.969532079658\n",
      "the rmse of the 17 th step on train data is:1508.6096457058504\n",
      "the rmse of the 18 th step on train data is:1491.3358300962575\n",
      "the rmse of the 19 th step on train data is:1476.4329819834122\n",
      "the rmse of the 20 th step on train data is:1462.5986842256716\n",
      "the rmse of the 21 th step on train data is:1450.3966332523325\n",
      "the rmse of the 22 th step on train data is:1438.9831803790396\n",
      "the rmse of the 23 th step on train data is:1428.2397460380234\n",
      "the rmse of the 24 th step on train data is:1419.4268044797172\n",
      "the rmse of the 25 th step on train data is:1410.6457505271346\n",
      "the rmse of the 26 th step on train data is:1402.4227587865482\n",
      "the rmse of the 27 th step on train data is:1395.7309708692212\n",
      "the rmse of the 28 th step on train data is:1389.1074497522816\n",
      "the rmse of the 29 th step on train data is:1382.6224590901602\n",
      "the rmse of the 30 th step on train data is:1377.220195653151\n",
      "the rmse of the 31 th step on train data is:1371.9741784902808\n",
      "the rmse of the 32 th step on train data is:1367.4112075636947\n",
      "the rmse of the 33 th step on train data is:1363.0032682089904\n",
      "the rmse of the 34 th step on train data is:1358.8726541479607\n",
      "the rmse of the 35 th step on train data is:1355.048668051879\n",
      "the rmse of the 36 th step on train data is:1351.8203945255552\n",
      "the rmse of the 37 th step on train data is:1348.6294963706457\n",
      "the rmse of the 38 th step on train data is:1345.5401207927796\n",
      "the rmse of the 39 th step on train data is:1342.7089666490942\n",
      "the rmse of the 40 th step on train data is:1340.2493455285592\n",
      "the rmse of the 41 th step on train data is:1337.8412676704713\n",
      "the rmse of the 42 th step on train data is:1335.855265071264\n",
      "the rmse of the 43 th step on train data is:1333.8057016244888\n",
      "the rmse of the 44 th step on train data is:1331.911616440298\n",
      "the rmse of the 45 th step on train data is:1330.306465579846\n",
      "the rmse of the 46 th step on train data is:1328.5312000281926\n",
      "the rmse of the 47 th step on train data is:1327.2100527461218\n",
      "the rmse of the 48 th step on train data is:1325.7245708509881\n",
      "the rmse of the 49 th step on train data is:1324.4945373617834\n",
      "the rmse of the 50 th step on train data is:1323.3351365099422\n",
      "the rmse of the 51 th step on train data is:1322.2268646203927\n",
      "the rmse of the 52 th step on train data is:1321.1190275264253\n",
      "the rmse of the 53 th step on train data is:1320.322040712013\n",
      "the rmse of the 54 th step on train data is:1319.358000695631\n",
      "the rmse of the 55 th step on train data is:1318.5919444236854\n",
      "the rmse of the 56 th step on train data is:1317.8502123358082\n",
      "the rmse of the 57 th step on train data is:1317.171404310436\n",
      "the rmse of the 58 th step on train data is:1316.4695719062297\n",
      "the rmse of the 59 th step on train data is:1315.8652568341095\n",
      "the rmse of the 60 th step on train data is:1315.357534260614\n",
      "the rmse of the 61 th step on train data is:1314.8206592769382\n",
      "the rmse of the 62 th step on train data is:1314.298541801356\n",
      "the rmse of the 63 th step on train data is:1313.890842241723\n",
      "the rmse of the 64 th step on train data is:1313.4775815548762\n",
      "the rmse of the 65 th step on train data is:1313.0543395321843\n",
      "the rmse of the 66 th step on train data is:1312.7172285806414\n",
      "the rmse of the 67 th step on train data is:1312.3837237061575\n",
      "the rmse of the 68 th step on train data is:1312.0535585748269\n",
      "the rmse of the 69 th step on train data is:1311.7518104934\n",
      "the rmse of the 70 th step on train data is:1311.4800259025499\n",
      "the rmse of the 71 th step on train data is:1311.233976014739\n",
      "the rmse of the 72 th step on train data is:1310.9948910311107\n",
      "the rmse of the 73 th step on train data is:1310.7910549449718\n",
      "the rmse of the 74 th step on train data is:1310.5867215354262\n",
      "the rmse of the 75 th step on train data is:1310.4154474282204\n",
      "the rmse of the 76 th step on train data is:1310.2266184390833\n",
      "the rmse of the 77 th step on train data is:1310.0770357582696\n",
      "the rmse of the 78 th step on train data is:1309.9182820208719\n",
      "the rmse of the 79 th step on train data is:1309.7805507985038\n",
      "the rmse of the 80 th step on train data is:1309.642202177852\n",
      "the rmse of the 81 th step on train data is:1309.5254563035116\n",
      "the rmse of the 82 th step on train data is:1309.4036456537954\n",
      "the rmse of the 83 th step on train data is:1309.3008821193052\n",
      "the rmse of the 84 th step on train data is:1309.2083754330324\n",
      "the rmse of the 85 th step on train data is:1309.1157299871643\n",
      "the rmse of the 86 th step on train data is:1309.0350904040085\n",
      "the rmse of the 87 th step on train data is:1308.962960644613\n",
      "the rmse of the 88 th step on train data is:1308.882715034828\n",
      "the rmse of the 89 th step on train data is:1308.8171540659616\n",
      "the rmse of the 90 th step on train data is:1308.7509749963258\n",
      "the rmse of the 91 th step on train data is:1308.6916155870535\n",
      "the rmse of the 92 th step on train data is:1308.6399112732024\n",
      "the rmse of the 93 th step on train data is:1308.5877346254858\n",
      "the rmse of the 94 th step on train data is:1308.5395017706865\n",
      "the rmse of the 95 th step on train data is:1308.4968028532135\n",
      "the rmse of the 96 th step on train data is:1308.4577025581045\n",
      "the rmse of the 97 th step on train data is:1308.4205011155361\n",
      "the rmse of the 98 th step on train data is:1308.3838996470924\n",
      "the rmse of the 99 th step on train data is:1308.3509588333327\n",
      "SVD trained\n"
     ]
    }
   ],
   "source": [
    "#实例化，并训练模型\n",
    "instc = svdRecsys()\n",
    "instc.train_SVD()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(1, '1704179171'),\n",
       " (1, '3118576636'),\n",
       " (1, '910169576'),\n",
       " (1, '2442660214'),\n",
       " (1, '612496662'),\n",
       " (1, '740269154'),\n",
       " (1, '1341096506'),\n",
       " (1, '3413747696'),\n",
       " (1, '2980258101'),\n",
       " (0.99736273762238137, '889355158'),\n",
       " (0.99259964755361663, '1270020700'),\n",
       " (0.99065498254976203, '3831661369'),\n",
       " (0.98870572009271285, '274750357'),\n",
       " (0.97686838817686461, '2311929369'),\n",
       " (0.96993014134767086, '4125420656'),\n",
       " (0.96598056485670247, '13857128'),\n",
       " (0.95947683156872077, '4273083332'),\n",
       " (0.95905905430925209, '326664129'),\n",
       " (0.95698323369087901, '1179584066'),\n",
       " (0.95579287264155832, '2367733143'),\n",
       " (0.95508138296776335, '1528500344'),\n",
       " (0.95494758289349491, '1876951633'),\n",
       " (0.95488613846741122, '4128714692'),\n",
       " (0.95388780043000232, '2862031808'),\n",
       " (0.95344937612973568, '40254649'),\n",
       " (0.9520220321165983, '738179988'),\n",
       " (0.95011453364688625, '1205670273'),\n",
       " (0.94546706456244689, '2814929505'),\n",
       " (0.94470154790508343, '2142755647'),\n",
       " (0.94469140146987152, '3158757965'),\n",
       " (0.94364247173432891, '3221950950'),\n",
       " (0.94226324919696591, '3982727529'),\n",
       " (0.94120263295862938, '3901918595'),\n",
       " (0.93997866317325196, '2575303578'),\n",
       " (0.9376573623886002, '228340462'),\n",
       " (0.93725607884305984, '4159897186'),\n",
       " (0.93444913425362297, '3108893200'),\n",
       " (0.93236793662844897, '3503491290'),\n",
       " (0.93127050554044433, '825378558'),\n",
       " (0.92925927967461885, '2413506174'),\n",
       " (0.92651398637607907, '1820037082'),\n",
       " (0.92337560615443881, '4201550847'),\n",
       " (0.92249743899756098, '862976415'),\n",
       " (0.92015404178342275, '4145779864'),\n",
       " (0.92001617406646641, '2352794406'),\n",
       " (0.91788035015245362, '4223848259'),\n",
       " (0.91671226705200037, '311120320'),\n",
       " (0.9154241998671836, '2270126516'),\n",
       " (0.91439390752581173, '45718516'),\n",
       " (0.90980587233901111, '1065552764'),\n",
       " (0.90735192359856287, '2970567256'),\n",
       " (0.9059094736508766, '635799819'),\n",
       " (0.9012301702704083, '399364804'),\n",
       " (0.90030572176443291, '3798981472'),\n",
       " (0.89959465983203957, '1370244570'),\n",
       " (0.89954254105481024, '495087397'),\n",
       " (0.89877614966452579, '3509100603'),\n",
       " (0.89850471031736323, '2153037761'),\n",
       " (0.89829519612077791, '671500966'),\n",
       " (0.89571585226599193, '3598553657')]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#利用训练的模型进行活动推荐\n",
    "Res_s = [] #定义空列表\n",
    "itemIndex = pickle.load(open(\"PE_eventIndex.pkl\", 'rb'))\n",
    "for itemId in itemIndex: \n",
    "    res = instc.svd_Based_CF(\"4236494\",itemId) #同其他两个协同过滤一样，随机指定一个用户，如\"4236494\"，对其进行活动推荐\n",
    "    Res_s.append((res, itemId)) #\n",
    "sorted(Res_s, key=lambda x: x[0],reverse=True)[:60] #将推荐的活动按照预估打分值进行排序，排在前面表示用户可能更感兴趣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从推荐结果来看，对随机指定的同一个用户，svd模型协同过滤推荐的活动排序与其他两个的推荐并不相同。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
